package com.inno.screenrecordhelper;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author (zijie)
 * @datetime 2021-11-20 13:58
 * @email hezijie@inno-cn.cn
 * @detali：
 */
public class ScreenRecordManager {
    private Context activity;
    private OnVideoRecordListener listener;
    private String savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator
            + "DCIM" + File.separator + "Camera";
    private String saveName = "inno_" + System.currentTimeMillis();
    public static final String TAG = "111";
    public static final int VIDEO_FRAME_RATE = 30;

    private File saveFile = null;

    private MediaProjectionManager mediaProjectionManager;
    private MediaRecorder mediaRecorder = null;
    private MediaProjection mediaProjection = null;
    private VirtualDisplay virtualDisplay = null;
    private DisplayMetrics displayMetrics;
    public boolean isRecording = false;
    public boolean recordAudio = true;//是否需要录制麦克风环境音量
    private TextView tvTime;

    public ScreenRecordManager(TextView tvTime,Context activity, OnVideoRecordListener listener, String savePath) {
        this.activity = activity;
        this.listener = listener;
        this.savePath = savePath;
        this.tvTime =tvTime;
        init();
    }


    public void init() {
        mediaProjectionManager = (MediaProjectionManager) activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        displayMetrics = new DisplayMetrics();
        WindowManager wm = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getMetrics(displayMetrics);
    }

    public void startRecord(int resultCode, Intent data) {
        Log.e(TAG, "startRecord:  录屏开始" );
        mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
        // 实测，部分手机上录制视频的时候会有弹窗的出现
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if (initScreenRecorder()) {
                    isRecording = true;
                    mediaRecorder.start();
                    listener.onStartRecord();
                    mTimeCount = 0;  //时间计数器重新赋值
                    mTimer = new Timer();
                    mTimer.schedule(new TimerTask() {
                        @Override
                        public void run() {
                            mTimeCount++;
                            Message message = handler.obtainMessage();
                            message.what = mTimeCount;
                            handler.sendMessage(message);

//                    tvRecordTime.setText(mTimeCount + "s");
                        }
                    }, 0, 1000);  //每隔1000毫秒就重复执行run函数
                } else {
                    showToast(R.string.phone_not_support_screen_record);
                }
            }
        }, 150);
    }

    public void onResume() {
        mediaRecorder.resume();
    }

    public void onPause() {
        mediaRecorder.pause();
    }


    private boolean initScreenRecorder() {
        Log.d(TAG, "initRecorder");
        boolean result = true;
        File f = new File(savePath);
        if (!f.exists()) {
            f.mkdirs();
        }
        saveName = "inno_" + System.currentTimeMillis();
        saveFile = new File(savePath, saveName + ".tmp");
        if (saveFile.exists()) {
            saveFile.delete();
        }
        mediaRecorder = new MediaRecorder();

        int width = Math.min(displayMetrics.widthPixels, 1080);
        int height = Math.min(displayMetrics.heightPixels, 1920);
        if (recordAudio) {
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        }
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        if (recordAudio) {
            mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        }
        mediaRecorder.setOutputFile(saveFile.getAbsolutePath());
        mediaRecorder.setVideoSize(width, height);
        mediaRecorder.setVideoEncodingBitRate(8 * 1024 * 1024);
        mediaRecorder.setVideoFrameRate(VIDEO_FRAME_RATE);
        try {
            mediaRecorder.prepare();
            virtualDisplay = mediaProjection.createVirtualDisplay("MainScreen", width, height, displayMetrics.densityDpi,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder.getSurface(), null, null);
            Log.d(TAG, "initRecorder 成功");
        } catch (IOException e) {
            Log.e(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
            e.printStackTrace();
            result = false;
        }
        return result;
    }

    public void clearAll() {
        mediaRecorder.release();
        mediaRecorder = null;
        virtualDisplay.release();
        virtualDisplay = null;
        mediaProjection.stop();
        mediaProjection = null;
    }

    /**
     * https://stackoverflow.com/questions/31572067/android-how-to-mux-audio-file-and-video-file
     */
    private void syntheticAudio(long audioDuration, long videoDuration, AssetFileDescriptor afdd) {
        Log.d(TAG, "start syntheticAudio");
        final File newFile = new File(savePath, "$saveName.mp4");
        if (newFile.exists()) {
            newFile.delete();
        }
        try {
            newFile.createNewFile();
            MediaExtractor videoExtractor = new MediaExtractor();
            videoExtractor.setDataSource(saveFile.getAbsolutePath());
            MediaExtractor audioExtractor = new MediaExtractor();
            audioExtractor.setDataSource(afdd.getFileDescriptor(), afdd.getStartOffset(), afdd.getLength() * videoDuration / audioDuration);
            MediaMuxer muxer = new MediaMuxer(newFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
            videoExtractor.selectTrack(0);
            MediaFormat videoFormat = videoExtractor.getTrackFormat(0);
            int videoTrack = muxer.addTrack(videoFormat);

            audioExtractor.selectTrack(0);
            MediaFormat audioFormat = audioExtractor.getTrackFormat(0);
            int audioTrack = muxer.addTrack(audioFormat);

            boolean sawEOS = false;
            int frameCount = 0;
            int offset = 100;
            int sampleSize = 1000 * 1024;
            ByteBuffer videoBuf = ByteBuffer.allocate(sampleSize);
            ByteBuffer audioBuf = ByteBuffer.allocate(sampleSize);
            MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
            MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();

            videoExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
            audioExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);

            muxer.start();

            // 每秒多少帧
            // 实测 OPPO R9em 垃圾手机，拿出来的没有 MediaFormat.KEY_FRAME_RATE
            int frameRate;
            if (videoFormat.containsKey(MediaFormat.KEY_FRAME_RATE)) {
                frameRate = videoFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
            } else {
                frameRate = 31;
            }
            // 得出平均每一帧间隔多少微妙
            long videoSampleTime = 1000 * 1000 / frameRate;
            while (!sawEOS) {
                videoBufferInfo.offset = offset;
                videoBufferInfo.size = videoExtractor.readSampleData(videoBuf, offset);
                if (videoBufferInfo.size < 0) {
                    sawEOS = true;
                    videoBufferInfo.size = 0;
                } else {
                    videoBufferInfo.presentationTimeUs += videoSampleTime;
                    videoBufferInfo.flags = videoExtractor.getSampleFlags();
                    muxer.writeSampleData(videoTrack, videoBuf, videoBufferInfo);
                    videoExtractor.advance();
                    frameCount++;
                }
            }
            boolean sawEOS2 = false;
            int frameCount2 = 0;
            while (!sawEOS2) {
                frameCount2++;
                audioBufferInfo.offset = offset;
                audioBufferInfo.size = audioExtractor.readSampleData(audioBuf, offset);

                if (audioBufferInfo.size < 0) {
                    sawEOS2 = true;
                    audioBufferInfo.size = 0;
                } else {
                    audioBufferInfo.presentationTimeUs = audioExtractor.getSampleTime();
                    audioBufferInfo.flags = audioExtractor.getSampleFlags();
                    muxer.writeSampleData(audioTrack, audioBuf, audioBufferInfo);
                    audioExtractor.advance();
                }
            }
            muxer.stop();
            muxer.release();
            videoExtractor.release();
            audioExtractor.release();

            // 删除无声视频文件
            if (null != saveFile)
            saveFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, "Mixer Error:${e.message}");
            // 视频添加音频合成失败，直接保存视频
            if (null != saveFile)
            saveFile.renameTo(newFile);

        } finally {
            try {
                afdd.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            new Handler().post(new Runnable() {
                @Override
                public void run() {
                    refreshVideo(newFile);
                    saveFile = null;
                }
            });
        }
    }

    public void stopRecord() {
        Log.e(TAG, "startRecord:  录屏结束" );
        if (mTimer != null) {
            mTimer.cancel();
        }
        stop();
        // saveFile
        if (saveFile != null) {
            File newFile = new File(savePath, saveName + ".mp4");
            // 录制结束后修改后缀为 mp4
            saveFile.renameTo(newFile);
            refreshVideo(newFile);
        }
        saveFile = null;

    }

    private void stop() {
        if (isRecording) {
            isRecording = false;
            try {
                if (null != mediaRecorder) {
                    mediaRecorder.setOnErrorListener(null);
                    mediaRecorder.setOnInfoListener(null);
                    mediaRecorder.setPreviewDisplay(null);
                    mediaRecorder.stop();
                    Log.d(TAG, "stop success");
                }
            } catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, "stopRecorder() error！${e.message}");
            } finally {
                if (null != mediaRecorder)
                    mediaRecorder.reset();
                if (null != virtualDisplay)
                    virtualDisplay.release();
                if (null != mediaProjection)
                    mediaProjection.stop();
                if (null != listener)
                    listener.onEndRecord();
            }


        }
    }

    private void refreshVideo(File newFile) {
        Log.d(TAG, "screen record end,file length:" + newFile.length());
        if (newFile.length() > 5000) {
            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            intent.setData(Uri.fromFile(newFile));
            activity.sendBroadcast(intent);
            showToast(R.string.save_to_album_success);
        } else {
            newFile.delete();
            showToast(R.string.phone_not_support_screen_record);
            Log.d(TAG, activity.getString(R.string.record_faild));
        }
    }

    private void showToast(int resId) {
        Toast.makeText(activity, activity.getResources().getString(resId), Toast.LENGTH_SHORT).show();
    }

    private Timer mTimer;  //计时器
    private int mTimeCount;  //时间计数

    private MyHandler handler = new MyHandler(this);
    static class MyHandler extends Handler{

        private WeakReference<ScreenRecordManager> weakReference;

        public MyHandler(ScreenRecordManager videoRecordManager) {
            this.weakReference = new WeakReference<>(videoRecordManager);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            ScreenRecordManager videoRecordManager = weakReference.get();
            videoRecordManager.tvTime.setText(msg.what + "s");
        }
    }


    public interface OnVideoRecordListener {
        /**
         * 录制开始时隐藏不必要的UI
         */
        void onBeforeRecord();

        /**
         * 开始录制
         */
        void onStartRecord();

        /**
         * 取消录制
         */
        void onCancelRecord();

        /**
         * 结束录制
         */
        void onEndRecord();
    }
}
